<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>操作符</title>
</head>
<body>
    <script>
        "use strict";
        /*
            ECMA-262描述了一组用于操作数据值的操作符，包括算术操作符（包括加号和减号）、位操作符、关系操作符和相等操作符等。
            ECMA-262操作符的与众不同之处在于，它们能够适用于很多值，例如字符串、数值、布尔值，甚至对象。不过，在应用于对象时，
            相应的操作符都会调用对象的valueOf()方法或toString()方法，以便取得可以操作的值。

            一元操作符：
                只能操作一个值的操作符叫做一元操作符。一元操作符是ECMAScript中最简单的操作符。

                1、递增和递减操作符 [++ --]
                    递增和递减操作符直接借鉴C，而且各有两个版本：前置型和后置型。顾名思义，前置型应该位于要操作的变量前面，
                    而后置型则应该位于要操作的变量之后。
                    
                    执行前置递增和递减操作时，变量的值都是在语句被求值之前改变的。
                    
                    后置型递增和递减操作符的语法不变，只不过要放在变量的后面，而不是前面，后置递增和递减与前置递增和递减有一个
                    非常重要的区别：即递增和递减操作是在包含他们的语句是在求值之后才执行的。

                    这4个操作符对任何值都适用，也就是他们不仅仅适用于整数，还可以用于字符串、布尔值、浮点值和对象。在应用于不同的值时，
                    递增和递减操作符遵循下列规则：
                        在应用于一个包含有效数字字符的字符串时，先将其转换为数字值，在执行加减1的操作。字符串变量变成数值变量
                        在应用于一个不包含有效数字字符的字符串时，将变量的值设置为NaN。字符串变量变成数值变量
                        在应用于布尔值false时，先将其转换为0再执行加减1的操作
                        在应用于布尔值true时，先将其转换为1再执行加减1的操作
                        在应用于浮点数值时，执行加减1的操作
                        在应用于对象时，先调用对象的valueOf()方法以取得一个可操作的值。然后对该值应用上述规则，如果结果是NaN，则再调用
                            toString()方法后再应用上述规则，对象变量最终变成数值变量。

                2、一元加和减操作符 [+ -]
                    绝大多数开发人员对一元加和减操作符都不会陌生，而且这两个ECMAScript操作符的作用与数学书上讲的完全一样。
                    
                    一元加操作符以一个加号(+)表示，放在数值前面，对数值不会产生任何影响。不过在对非数值应用一元加操作符时，
                    该操作符会像Number()转型函数一样对这个值执行转换。换句话说，布尔值false和true将会被转换成0和1，字符串值
                    会被按照一种特殊的规则解析，而对象是先调用valueOf()方法或toString()方法，在转换得到值。

                    一元减操作符主要用于表示负数。在将一元减操作符应用于数值时，该值会变成负数，二挡应用于非数值时，一元减操作符遵循
                    与一元加操作符相同的规则，最后再将得到的数值转换成负数。

            位操作符：
                位操作符作用域最基本的层次上，即按内存中表示数值的位来操作数值。ECMAScript中的所有数值都以IEEE-754 64位格式存储，但
                位操作符并不直接操作64位的值。而是现将64位的值转换为32位的整数，然后再执行操作，最后再将结果转换回64位。对于开发人员来说，
                64位存储格式是透明的，因此整个过程就像是操作32位整数一样。

                对于有符号的整数，32位中的前31位用于保存整数的值，第32位用于保存整数的符号：0表示正数，1表示负数。这个表示符号的位叫做符号位。
                符号位的值决定了其他位的格式。其中正数以纯二进制格式存储，31位中的每一位都是2的幂...

                负数同样以二进制码存储，但使用的格式是二进制补码。计算一个数值的二进制补码，需要经历下面3个步骤：
                    1、求这个数绝对值的二进制码
                    2、求二进制反码，即0变1,1变0
                    3、得到二进制的反码加1

                在以二进制字符串的形式输出一个负数时，我们看到的只是这个负数绝对值的二进制码前面加一个负号

                默认情况下，在ECMAScript中的所有整数都是有符号整数。

                在ECMAScript中，当对数值应用位操作符时，后台会发生如下转换过程:
                    64位的数值被转换成32位的数值，然后执行位操作，最后再将32位的结果转换回64位。
                    这样，表面上看起来像是在操作32位数值，就跟在其他语言中以类似方式执行二进制操作结果一样。
                    但这个转换过程也导致了一个严重的副效应，即在对特殊的NaN、Infinity值应用位操作时，这两个值都会被当做0来处理。
                
                如果对非数值应用位操作符，会先使用Number()函数将该值转换为一个数值（自动完成），然后在应用为操作，得到的结果将是一个数值。

                1、按位非 [~]
                    按位非操作符用一个波浪线表示(~)，执行按位非的结果就是返回数值的反码

                2、按位与 [&]
                    按位与操作符用一个和负号表示(&)，他有两个操作数。

                    第一个数值的位  第二个数值的位      结果
                        1               1              1
                        1               0              0
                        0               1              0
                        0               0              0

                    结论：按位与操作只有在两个数值对应的位都是1，才返回1，否则返回0

                3、按位或 [|]
                    按位或操作符由一个竖线符号(|)表示，同样也有两个操作数。

                    第一个数值的位  第二个数值的位      结果
                        1               1              1
                        1               0              1
                        0               1              1
                        0               0              0
                    
                    结论：按位或操作在有一个位是1的情况下返回1，而只有在两个位都是0的情况下才返回0

                4、按位异或 [^]
                    按位异或操作符是由一个插入符号(^)表示，也有两个操作数。

                    第一个数值的位  第二个数值的位      结果
                        1               1              0
                        1               0              1
                        0               1              1
                        0               0              0

                    结论：按位异或操作符在只有1个位是1的情况下才返回1，其他情况返回0

                5、左移 [<<]
                    左移操作符由两个小于号(<<)表示，这个操作符会将数值的所有位(符号位不变)向左移动指定的位数，右侧空出的位用0来填充
                    左移不会影响操作数的符号位

                6、有符号右移 [>>]
                    有符号右移以两个大于号(>>)表示，这个操作符会将数值享有移动指定的位数，保留符号位，在位移过程中产生的空位位于符号位的右侧
                    原数值的左侧，而此时ECMAScript会用符号位来填充所有空位。

                7、无符号右移 [>>>]
                    无符号右移操作符由3个大于号(>>>)表示，这个操作符会将数值的所有32位都向右移动，空出的位用0填充

            布尔操作符：
                在一门编程语言中，布尔操作符的重要性堪比相等操作符。如果没有测试两个值关系的能力，那么诸如if...else和循环之类的语句
                就没有用武之地了。布尔操作符一共有3个：非(NOT)、与(AND)、或(OR)

                1、逻辑非 [!]
                    逻辑非操作符由一个感叹号(!)表示，可以应用于ECMAScript中的任何值。无论这个值是什么数据类型，这个操作符都会返回一个布尔值。
                    逻辑非操作符首先会将他的操作数转换成布尔值，然后在对其求反。逻辑非操作符遵循以下规则：
                        * 如果操作数是一个对象，则返回false
                        * 如果操作数是一个空字符串""，则返回true
                        * 如果操作数是一个非空字符串，则返回false
                        * 如果操作数是数值0，则返回true
                        * 如果操作数是任意非0数值，包括Infinity，返回false
                        * 如果操作数是null，则返回true
                        * 如果操作数是NaN，则返回true
                        * 如果操作数是undefined，则返回true

                2、逻辑与 [&&]
                    逻辑与操作符由两个和号(&&)表示，由两个操作数：
                        第一个操作数    第二个操作数    结果
                        true           true           true
                        true           false          false
                        false          true           false
                        false          false          false
                    
                    逻辑与操作符可以应用于任何类型的操作数，而不仅仅是布尔值。在有一个操作数不是布尔值的情况下，逻辑与操作就不一定返回布尔值。
                    此时，他遵循以下规则：
                        * 如果第一个操作数是对象，则返回第二个操作数
                        * 如果第二个操作数是对象，则只有在第一个操作数的求职结果为true的情况下才返回该对象
                        * 如果两个操作数都是对象，则返回第二个操作数
                        * 如果第一个操作数是null，则返回null
                        * 如果第一个操作数是NaN，则返回NaN
                        * 如果第一个操作数是undefined，则返回undefined
                    逻辑与操作属于短路操作，即如果第一个操作数如果能够决定结果那么就不会再对第二个操作数求职。对于逻辑与操作而言，如果第一个操作数的求职结果为false，
                    则无论第二个操作数是什么值，结果都不再可能是true了
                
                3、逻辑或 [||]
                    逻辑或操作符由两个竖线符号(||)表示，由两个操作数：
                        第一个操作数    第二个操作数    结果
                        true           true           true
                        true           false          true
                        false          true           true
                        false          false          false

                    与逻辑操作符类似，如果有一个操作数不是布尔值，逻辑与也不一定返回布尔值，规则如下：
                        * 如果第一个操作数是对象，则返回第一个操作数
                        * 如果第一个操作数的求值结果为false，则返回第二个操作数
                        * 如果两个操作数都是对象，则返回第一个操作数
                        * 如果两个操作数都是null，则返回null
                        * 如果两个操作数都是NaN，则返回NaN
                        * 如果两个操作数都是undefined，则返回undefined
                    与逻辑与操作符类似，逻辑或操作符也是短路操作符，也就是说，如果第一个操作数的求值结果是true，也就不会对第二个操作数求值了。
                    利用逻辑或的这一行为来避免为变量赋null或undefined值。
                        var myObject = preferredObject || backupObject;

            乘性操作符：
                ECMAScript定义了3个乘性操作符：乘法、除法和求模。这些操作符与Java、C和Perl中的相应操作符用途类似，只不过在操作数未非数值的情况下
                会执行自动的类型转换，如果参与乘性计算的某个操作数不是数值，后台回先使用Number()转型函数将其转换为数值。也就是说""当做0来处理，
                布尔值true和false分别转换成1和0来处理。

                1、乘法 [*]
                    乘法操作符由一个星号(*)表示，用于计算两个数的乘积，在处理特殊数值的情况下，乘性操作符遵循以下特殊的规则：
                        * 如果操作数都是数值，执行常规的乘法计算，即两个正数或两个负数相乘的结果还是正数，而如果只有一个操作数是负号，那么结果就是负数。
                            如果乘积超出了ECMAScript数值的表示范围，则返回Infinity或-Infinity。
                        * 如果一个操作数是NaN，则结果就是NaN
                        * 如果是Infinity和0相乘，则结果是NaN
                        * 如果Infinity和非0相乘，则结果是Infinity或-Infinity，取决于有符号操作数的符号
                        * 如果是Infinity与Infinity相乘，则结果是Infinity
                        * 如果有一个操作数不是数值，则再后台调用Number()函数将其转换为数值，然后在应用上面的规则

                2、除法 [/]
                    除法操作符是由一个斜线符号(/)表示，执行第二个操作数除第一个操作数的计算。
                    与乘法操作符类似，除法操作符对特殊的值也有特殊的处理规则：
                        * 如果操作数都是数值，执行常规除法计算，即两个正数或两个负数相除的结果还是正数，而如果只有一个操作数有符号，那么结果就是负数。
                            如果商超出了ECMAScript数值的表示范围，则返回Infinity或-Infinity。
                        * 如果一个操作数是NaN，则结果就是NaN
                        * 如果是Infinity被Infinity除，则结果是NaN
                        * 如果是零被零除，则结果是NaN
                        * 如果是非零的有限数被零除，则结果是Infinity或-Infinity，取决于有符号操作数的符号
                        * 如果是Infinity被任何非零数值除，则结果是Infinity或-Infinity，取决于有符号操作数的符号
                        * 如果有一个操作数不是数值，则在后台调用Number()将其转换为数值，在应用上面的规则

                3、求模 [%]
                    求模(余数)操作符由一个百分号(%)表示，与另外两个乘性操作符相似，求模操作符会遵循以下特殊规则来处理：
                        * 如果操作数都是数值，执行常规的除法计算，返回除得的余数
                        * 如果被除数是无穷大值而除数是有限大的值，则结果是NaN
                        * 如果被除数是又穷大的数值而除数是零，则结果是NaN
                        * 如果是Infinity被Infinity除，结果是NaN
                        * 如果被除数是有限大的数值而除数是无穷大的值，结果是被除数
                        * 如果被除数是零，则结果是零
                        * 如果有一个操作数不是数值，则在后台调用Number()将其转换为数值，然后在应用上面的规则

            加性操作符：
                加法和减法这两个加性操作符应该说是编程语言中最简单的算术操作符了。但是在ECMAScript中，这两个操作符都有一系列的特殊行为。
                与乘性操作符类似，加性操作符也会在后台转换不同的数据类型。然而，对于加性操作符而言，相应的转换规则还是有些复杂。

                1、加法 [+]
                    加法操作符由加号(+)表示，用法如下：
                    
                    如果两个操作数都是数值，执行常规的加法计算，然后根据下列规则返回结果：
                        * 如果有一个操作数是NaN，则结果是NaN
                        * 如果是Infinity加Infinity，则结果是Infinity
                        * 如果是-Infinity加-Infinity，则结果是-Infinity
                        * 如果是Infinity加-Infinity，则结果是NaN
                        * 如果是+0加+0，则结果是+0
                        * 如果是-0加-0，则结果是-0
                        * 如果是+0加-0，则结果是+0
                    不过，如果有一个操作数是字符串，那么就要应用如下规则：
                        * 如果两个操作数都是字符串，则将第二个操作数与第一个操作数拼接起来
                        * 如果只有一个操作数是字符串，则将另一个操作数转换为字符串，然后再将两个操作数拼接起来
                    如果有一个操作数是对象、数值或布尔值，则调用他们的toString()方法取得相应的字符串值，然后在应用前面关于字符串的规则。
                    对于undefined和null，则分别调用字符串函数String()并取得字符串"undefined"和"null"

                2、减法 (-)
                    减法操作符是另一个极为常用的操作符，与加法操作符类似，ECMAScript中的操作符在处理各种数据类型转换时，同样需要遵循一些特殊规则：
                        * 如果两个操作数都是数值，则执行常规的算术减法操作并返回相应的结果
                        * 如果有一个操作数是NaN，则结果是NaN
                        * 如果是Infinity减Infinity，则结果是NaN
                        * 如果是-Infinity减-Infinity，则结果是NaN
                        * 如果是Infinity减-Infinity，则结果是Infinity
                        * 如果是-Infinity减Infinity，则结果是-Infinity
                        * 如果是+0减+0，则结果是+0
                        * 如果是-0减+0，则结果是-0
                        * 如果是-0减-0，则结果是+0
                        * 如果有一个操作数是字符串、布尔值、null或undefined，则现在后台调用Number()函数将其转换为数值，然后在管局前面的规则执行算术减法运算，
                            如果转换的结果是NaN，则减法的结果就是NaN
                        * 如果有一个操作符是对象，则调用对象的valueOf()方法以取得表示该对象的数值。如果得到的值是NaN，则减法的结果就是NaN，如果对象没有valueOf()
                            方法，则调用toString()方法并将得到的字符串转换为数值    

            关系操作符：
                小于(<)、大于(>)、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个值得比较，比较的规则与我们在数学课上所学的一样，这几个操作符都返回一个布尔值。
                与ECMAScript中的其他操作符一样，当关系操作符的操作数使用了非数值时，也要进行数据转换或完成某些奇怪的操作，以下就是相应的规则：
                    * 如果两个操作数都是数值，则进行数值比较
                    * 如果两个操作数都是字符串，则比较两个字符串对应的字符编码值
                    * 如果一个操作数是数值，则将另一个操作数也转换成数值，然后执行数值比较
                    * 如果一个操作数是对象，则调用这个对象的valueOf()方法，用得到的结果按照前面的规则执行比较。如果对象没有valueOf()方法，则调用toString()
                        方法，并用得到的结果根据前面的规则执行比较
                    * 如果一个操作数是布尔值，则先将其转换为数值，然后再执行比较

                任何操作数与NaN进行比较，结果都是false

            相等操作符：
                确定搞两个变量是否相等，是编程语言中非常重要的一个操作。在比较字符串、数值和布尔值的相等性时，问题还比较简单。但是在涉及到对象的比较时，
                就比较复杂了。最早的ECMAScript的相等和不相等操作符会在执行比较之前，现将对象转换成相似的类型。后来有人提出了这种转换到底是否合理的质疑。
                最后，ECMAScript的解决方案就是提供两组操作符：相等和不相等--先转换再比较，全等和不全等--仅比较不转换

                1、相等(==)和不相等(!=)
                    ECMAScript中的相等操作符由两个等于号(==)表示，如果两个操作数相等，则返回true，而不相等操作符由感叹号后跟一个等号(!=)表示，
                    如果两个操作数不相等，则返回true。这两个操作符都会先转换操作数（通常称为强制转型），然后再比较他们的相等性。

                    在转换不同的数据类型时，相等和不相等操作符遵循以下基本规则：
                        * 如果有一个操作数是布尔值，则在比较相等性之前先将其转换为数值--false转换成0，而true转换为1
                        * 如果一个操作数是字符串，而另一个操作数是数值，在比较相等性之前现将字符串转换为数值
                        * 如果一个操作数是对象，另一个操作数不是，则调用对象的valueOf()方法用得到的基本类型值按照前面的规则进行比较

                    这两个操作符在进行比较时要遵循下列规则：
                        * null和undefined是相等的
                        * 要比较相等性之前，不能讲null和undefined转换成其他任何值
                        * 如果有一个操作数是NaN，则相等操作符返回false，而不相等操作符返回true。重要提示：
                            及时两个操作数都是NaN，相等操作符也返回false，因为按照规则，NaN不等于NaN
                        * 如果两个操作数都是对象，则比较他们是不是同一个对象。如果两个操作数都是指向同一个对象，则相等操作符返回true，而不相等操作符返回false

                    下表列出了一些特殊值得比较结果：
                        表达式                   值      
                        null == undefined       true
                        "NaN" == NaN            false
                        5 == NaN                false
                        NaN == NaN              false
                        NaN != NaN              true
                        false == 0              true
                        true == 1               true   
                        true == 2               false
                        undefined == 0          false
                        null == 0               false
                        "5" == 5                true

                2、全等(===)和不全等(!==)
                    除了在比较之前不转换操作数之外，全等和不全等操作符与相等和不相等操作符没有什么区别。
                    全等操作符由三个等号(===)表示，它只在两个操作数未经转换就想等的情况下才返回true
                    不全等操作符由一个叹号后跟两个等号(!==)表示，它在两个操作数未经转换就不相等的情况下才返回true

                    结论：null == undefined会返回true，因为他们是类似的值，但null===undefined会返回false，因为他们是不同类型的值

            条件操作符：
                条件操作符应该算是ECMAScript中最灵活的一种操作符了，而且他遵循与Java中的条件操作符相同的语法形式，如下面的例子所示：
                    variable = boolean_expression ? true_value : false_value;
                
                本质上，这行代码的含义就是基于对boolean_expression求值的结果，决定给变量variable赋什么值。
                如果求值结果为true，则给变量variable赋true_value的值；
                如果求值结果为false，则给变量variable赋false_value的值。

            赋值操作符：
                简单的赋值操作符由等于号(=)表示，起作用就是把右侧的值赋给左侧的变量，如下面的例子：
                    var num = 20;

                如果在等于号(=)前面再添加乘性操作符、加性操作符或位操作符，就可以完成复合赋值操作，例如：
                    var num = 10;
                    num = num + 10; --> 使用复合操作符表示： num += 10;

                每个主要算术操作符（以及个别其他的操作符）都有对应的复合操作符。这些操作符如下所示：
                    * 乘/赋值 (*=)
                    * 除/赋值 (/=)
                    * 模/赋值 (%=)
                    * 加/赋值 (+=)
                    * 减/赋值 (-=)
                    * 左移/赋值 (<<=)
                    * 有符号右移/赋值 (>>=)
                    * 无符号右移/赋值 (>>>=)
                
                设计这些操作符的主要目的就是简化赋值操作，使用它们不会带来任何性能提升。

            逗号操作符：
                使用逗号操作符可以在一条语句中执行多个操作，如下面的例子所示：
                    var num1 = 1, num2 = 5, num3 = 10;

                逗号操作符多用于声明多个变量；但除此之外，逗号操作符还可以用作赋值。在用于赋值时，逗号操作符总会返回表打死中的最后一项，如下面的例子所示：
                    var num = (5, 1, 4, 8, 0); // num的值为0

                由于0是表达式中的最后一项，因此num的值就是0。虽然逗号的这种方式并不常见，但是这个例子可以帮助我们理解逗号的这种行为。


        */
        
        var num = (5, 1, 4, 8, 0);
        console.log(num); // 0
    </script>
</body>
</html>